﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using MasterDetailTemplate.Confidential;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using Event = MasterDetailTemplate.Models.Event;

namespace MasterDetailTemplate.Services.Implementations {
    /// <summary>
    /// OneDrive收藏存储（无法单元测试）
    /// </summary>
    public class OneDriveEventStorage : IRemoteEventStorage {
        /// <summary>
        /// 状态。
        /// </summary>
        public string Status {
            get => _status;
            private set {
                _status = value;
                StatusChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 状态。
        /// </summary>
        private string _status;

        /// <summary>
        /// 状态改变事件。
        /// </summary>
        public event EventHandler StatusChanged;

        /// <summary>
        /// 获得所有事件。
        /// </summary>
        public async Task<IList<Event>> GetEventsAsync() {
            var rootChildren = await _graClient.Me.Drive.Root.Children.Request()
                .GetAsync();
            if (!rootChildren.Any(p => p.Name == "RememberTheTime.zip")) {
                return new List<Event>();
            }

            var fileStream = await _graClient.Me.Drive.Root
                .ItemWithPath("/RememberTheTime.zip").Content.Request()
                .GetAsync();

            ZipInputStream zipStream = new ZipInputStream(fileStream);
            ZipEntry zipEntry = zipStream.GetNextEntry();

            if (zipEntry == null) {
                return new List<Event>();
            }

            byte[] buffer = new byte[1024];
            var jsonStream = new MemoryStream();
            await Task.Run(
                () => StreamUtils.Copy(zipStream, jsonStream, buffer));

            zipStream.Close();
            fileStream.Close();

            jsonStream.Position = 0;
            var jsonReader = new StreamReader(jsonStream);
            var eventsList = JsonConvert.DeserializeObject<IList<Event>>(
                await jsonReader.ReadToEndAsync());

            jsonReader.Close();
            jsonStream.Close();

            return eventsList ?? new List<Event>();
        }

        /// <summary>
        /// 一次性保存所有事件。
        /// </summary>
        /// <param name="events">所有的事件。</param>
        public async Task SaveEventsAsync(IList<Event> events) {
            var json = JsonConvert.SerializeObject(events);

            //先压缩
            MemoryStream fileStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(fileStream);
            zipStream.SetLevel(3);

            ZipEntry newEntry = new ZipEntry("RememberTheTime.json");
            newEntry.DateTime = DateTime.Now;
            zipStream.PutNextEntry(newEntry);

            var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
            await Task.Run(() =>
                StreamUtils.Copy(jsonStream, zipStream, new byte[1024]));

            jsonStream.Close();
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = false;
            zipStream.Close();

            //上传
            Status = "正在上传远程日期事件";
            fileStream.Position = 0;
            try {
                await _graClient.Me.Drive.Root
                    .ItemWithPath("/RememberTheTime.zip").Content.Request()
                    .PutAsync<DriveItem>(fileStream);
            } catch (Exception e) {
                //TODO Handle exception
                throw new NotImplementedException();
            } finally {
                fileStream.Close();
            }
        }

        /// <summary>
        /// Microsoft Graph访问权限数组。
        /// </summary>
        private string[] _scopes = OneDriveOAuthSettings.Scopes.Split(' ');

        /// <summary>
        /// Microsoft Graph身份验证工具
        /// </summary>
        private IPublicClientApplication _pca;

        /// <summary>
        /// Microsoft Graph客户端。
        /// </summary>
        private GraphServiceClient _graClient;

        /// <summary>
        /// 登录。
        /// </summary>
        public async Task<bool> SignInAsync() {
            Status = "正在连接Onedrive";
            try {
                var interactiveRequest = _pca.AcquireTokenInteractive(_scopes);

                //专门针对android
                if (App.AuthUIParent != null) {
                    interactiveRequest =
                        interactiveRequest.WithParentActivityOrWindow(
                            App.AuthUIParent);
                }

                await interactiveRequest.ExecuteAsync();
            } catch (Exception e) {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 注销。
        /// </summary>
        public async Task SignOutAsync() {
            Status = "正在退出OneDrive登录";
            var accounts = await _pca.GetAccountsAsync();
            while (accounts.Any()) {
                await _pca.RemoveAsync(accounts.First());
                accounts = await _pca.GetAccountsAsync();
            }
        }

        /// <summary>
        /// 是否已经登录
        /// </summary>
        public async Task<bool> IsSignedInAsync() {
            Status = "正在检查OneDrive的登录状态";
            string accessToken = string.Empty;
            try {
                var accounts = await _pca.GetAccountsAsync();
                if (accounts.Any()) {
                    var silentAuthResult = await _pca
                        .AcquireTokenSilent(_scopes, accounts.FirstOrDefault())
                        .ExecuteAsync();
                    accessToken = silentAuthResult.AccessToken;
                }
            } catch (MsalUiRequiredException e) {
                return false;
            }

            return !string.IsNullOrEmpty(accessToken);
        }

        /// <summary>
        /// OneDrive收藏存储
        /// </summary>
        public OneDriveEventStorage() {
            var builder =
                PublicClientApplicationBuilder.Create(OneDriveOAuthSettings
                    .ApplicationId);

            _graClient = new GraphServiceClient(
                new DelegateAuthenticationProvider(async (requestMessage) => {
                    var accounts = await _pca.GetAccountsAsync();
                    var result = await _pca
                        .AcquireTokenSilent(_scopes, accounts.FirstOrDefault())
                        .ExecuteAsync();
                    requestMessage.Headers.Authorization =
                        new AuthenticationHeaderValue("Bearer",
                            result.AccessToken);
                }));

            _pca = builder.Build();
        }
    }
}